package com.gitee.qdbp.jdbc.plugins.impl;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitee.qdbp.jdbc.model.SqlFile;
import com.gitee.qdbp.jdbc.plugins.SqlFileScanner;
import com.gitee.qdbp.tools.files.PathTools;
import com.gitee.qdbp.tools.utils.ConvertTools;
import com.gitee.qdbp.tools.utils.DateTools;
import com.gitee.qdbp.tools.utils.StringTools;
import com.gitee.qdbp.tools.utils.VerifyTools;

/**
 * SQL模板文件扫描接口的基础实现类
 *
 * @author zhaohuihua
 * @version 20200830
 * @since 3.2.0
 */
public abstract class BaseSqlFileScanner implements SqlFileScanner {

    private static final Logger log = LoggerFactory.getLogger(BaseSqlFileScanner.class);

    /** 文件路径扫描规则, 多个以逗号分隔 **/
    // settings/sqls/**/*.sql, com/gitee/qdbp/jdbc/test/**/*Dao.xml
    private String patterns;

    public BaseSqlFileScanner() {
    }

    public BaseSqlFileScanner(String patterns) {
        this.patterns = patterns;
    }

    /**
     * 扫描SQL文件
     *
     * @param pattern 文件路径扫描规则
     * @return SQL文件URL路径
     * @throws IOException IO异常
     */
    protected abstract List<URL> scanSqlFiles(String pattern) throws IOException;

    @Override
    public List<SqlFile> scanSqlFiles(Date lastUpdateTime) throws IOException {
        String pattern = VerifyTools.nvl(this.patterns, "settings/sqls/**/*.sql");
        String[] patterns = StringTools.split(pattern, ',');
        Date startTime = new Date();
        Long minTime = lastUpdateTime == null ? null : lastUpdateTime.getTime();
        // 扫描文件
        List<SqlFile> sqlFiles = scanSqlFiles(minTime, patterns);
        // 输出日志
        if (lastUpdateTime == null) {
            if (sqlFiles.isEmpty()) {
                if (log.isWarnEnabled()) {
                    log.warn("Failed to scan sql templates, no files were found.\n\tpatterns is {}", pattern);
                }
            } else if (log.isTraceEnabled()) {
                String duration = ConvertTools.toDuration(startTime, true);
                String msg = "Success to scan sql templates,  elapsed time {}, total of {} files were found.\n\t{}";
                log.trace(msg, duration, sqlFiles.size(), collectSqlFilePaths(sqlFiles));
            } else if (log.isInfoEnabled()) {
                String duration = ConvertTools.toDuration(startTime, true);
                String msg = "Success to scan sql templates,  elapsed time {}, total of {} files were found.";
                log.info(msg, duration, sqlFiles.size());
            }
        } else {
            if (log.isTraceEnabled() && !sqlFiles.isEmpty()) {
                String duration = ConvertTools.toDuration(startTime, true);
                String updateTime = DateTools.toNormativeString(lastUpdateTime);
                String msg = "Success to scan sql templates,  elapsed time {}, " +
                        "total of {} files with update time > {} were found.\n\t{}";
                log.trace(msg, duration, sqlFiles.size(), updateTime, collectSqlFilePaths(sqlFiles));
            } else if (log.isInfoEnabled()) {
                String duration = ConvertTools.toDuration(startTime, true);
                String updateTime = DateTools.toNormativeString(lastUpdateTime);
                String msg = "Success to scan sql templates,  elapsed time {}, " +
                        "total of {} files with update time > {} were found.";
                log.info(msg, duration, sqlFiles.size(), updateTime);
            }
        }
        return sqlFiles;
    }

    protected String collectSqlFilePaths(List<SqlFile> sqlFiles) {
        List<String> filePaths = new ArrayList<>();
        for (SqlFile sqlFile : sqlFiles) {
            filePaths.add(PathTools.getClasspathRelativePath(sqlFile.getAbsolutePath()));
        }
        return ConvertTools.joinToString(filePaths);
    }

    protected List<SqlFile> scanSqlFiles(Long minTime, String[] patterns) throws IOException {
        List<SqlFile> sqlFiles = new ArrayList<>();
        for (String pattern : patterns) {
            List<URL> urls = scanSqlFiles(pattern);
            if (urls != null && !urls.isEmpty()) {
                for (URL url : urls) {
                    SqlFile sqlFile = parseSqlFile(pattern, url);
                    if (minTime == null) {
                        sqlFiles.add(sqlFile);
                    } else if (sqlFile.getLastUpdateTime() != null && sqlFile.getLastUpdateTime() > minTime) {
                        sqlFiles.add(sqlFile);
                    }
                }
            }
        }
        return sqlFiles;
    }

    protected SqlFile parseSqlFile(String folder, URL url) {
        String absolutePath = PathTools.toUriPath(url);
        String relativePath = getSqlRelativePath(folder, url);
        Date lastUpdateTime = PathTools.getLastModified(url);
        SqlFile sqlFile = new SqlFile();
        sqlFile.setUrlPath(url);
        sqlFile.setAbsolutePath(absolutePath);
        sqlFile.setRelativePath(relativePath);
        if (lastUpdateTime != null) {
            sqlFile.setLastUpdateTime(lastUpdateTime.getTime());
        }
        return sqlFile;
    }

    /** 获取SQL相对路径 **/
    protected String getSqlRelativePath(String pattern, URL url) {
        String urlPath = url.toString();
        // jar:file:/E:/repository/com/gitee/qdbp/xxx-1.0.0.jar!/settings/sqls/xxx.xml
        // file:/D:/qdbp/qdbp-jdbc/jdbc-core/target/classes/settings/sqls/xxx.xml
        String lowerCase = urlPath.toLowerCase();
        String jarFlag = ".jar!/";
        int jarIndex = lowerCase.indexOf(jarFlag);
        if (jarIndex > 0) {
            int folderIndex = lowerCase.lastIndexOf('/', jarIndex);
            if (folderIndex >= 0) {
                // xxx-1.0.0.jar!/settings/sqls/xxx.xml
                return urlPath.substring(folderIndex + 1);
            }
        }
        int classesIndex = lowerCase.indexOf("/classes/");
        if (classesIndex > 0) {
            // settings/sqls/xxx.xml
            return urlPath.substring(classesIndex + "/classes/".length());
        }
        // 既不是jar路径也不是classes路径
        String folder = pattern;
        int starIndex = pattern.indexOf('*');
        if (starIndex > 0) {
            folder = pattern.substring(0, starIndex);
        }
        int folderIndex = urlPath.indexOf(folder);
        if (folderIndex > 0) {
            return StringTools.removeLeft(urlPath.substring(folderIndex), '/');
        }
        return urlPath;
    }

    /** 文件路径扫描规则, 多个以逗号分隔 **/
    public String getPatterns() {
        return patterns;
    }

    /** 文件路径扫描规则, 多个以逗号分隔 **/
    public void setPatterns(String patterns) {
        this.patterns = patterns;
    }

}
